package com.szwistar.common.datastruct;

import java.util.Hashtable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 可带单位的参数类型
 * <li>此类型的参数可能形如 "30.72e-6THz", "0.03072gHz", "30.72 mhZ", "30720000 hz", "30720000" 等
 * <li>可以带单位（单位前的空白字符可忽略，单位大小写不敏感，支持 Hz/KHz/MHz/GHz/THz），也可以不带单位（默认为 Hz）
 * <li>注意：参数值保存在一个 long 和一个 double 型变量中，太大的数值可能导致 getLongValue() 返回为 null ！
 * <li>注意：对于有效位数很多的频率数值 getDoubleValue() 可能无法保持足够的精度 ！
 */
public abstract class NumericWithUnit {
    // 默认的字符串格式正则表达式
    static final Pattern defaultPattern = Pattern.compile("([\\d.eE+-]+)\\s*(\\w*)");
    String unit = "";       // 当前数值所使用的单位
    Long lval = null;       // 用整形表示的数值
    double dval = 0.0d;     // 用浮点数表示的数值

    public String getUnit() { return unit; }
    public Long getLongValue() { return lval; }
    public double getDoubleValue() { return dval; }

    public NumericWithUnit() { unit = getStdUnit(); }
    public NumericWithUnit(int value) { this(); this.dval = value; this.lval = (long) value; }
    public NumericWithUnit(long value) { this(); this.dval = value; this.lval = (long) value; }
    public NumericWithUnit(float value) { this(); this.dval = value; if(dval <= Long.MAX_VALUE) this.lval = (long) value; }
    public NumericWithUnit(double value) { this(); this.dval = value; if(dval <= Long.MAX_VALUE) this.lval = (long) value; }

    /**
     * 获取本数据的字符串正则表达式模式
     */
    protected Pattern getFormatPattern() { return defaultPattern; }

    /**
     * 获取国际标准单位
     */
    public abstract String getStdUnit();

    /**
     * 获取指定单位转换到默认单位的系数
     * @param unit2 指定单位
     * @return 转换到默认单位的系数
     */
    protected abstract double unitFactor(String unit2);

    /**
     * 从当前单位转换到指定的单位
     */
    public NumericWithUnit convertToUnit(String unit2) {
        if(unit.equalsIgnoreCase(unit2)) return this;
        double factor  = unitFactor(unit);
        double factor2 = unitFactor(unit2);
        dval = dval * factor / factor2;
        lval = (dval <= Long.MAX_VALUE) ? Math.round(dval) : null;
        this.unit = unit2;
        return this;
    }

    /**
     * 转换为国际标准单位
     */
    public NumericWithUnit convertToStdUnit() { return convertToUnit(getStdUnit()); }

    /**
     * 解析字符串并创建
     * @param arg 字符串格式的数值表达式。如果未带单位，则默认为国际标准单位。
     * @throws NumberFormatException 字符串解析失败
     */
    public NumericWithUnit(String arg) throws NumberFormatException {
        this();
        Matcher matcher = getFormatPattern().matcher(arg);
        if(!matcher.find()) throw new NumberFormatException();
        String num = matcher.group(1);
        String unit2 = matcher.group(2);
        if(Utils.isNotEmpty(unit2)) this.unit = unit2; // 未带单位，则默认为标准单位
        //System.out.println("'" + num + "' '" + unit + "'");

        lval = null;
        dval = Double.parseDouble(num);
        if(dval <= Long.MAX_VALUE) {
            // 数值大小可以用 long 表示
            try {
                // 尝试以 long 型表示，以保持精度
                lval = Long.parseLong(num);
            } catch(NumberFormatException e) {
                // 以 long 型解析失败，则使用 double 型值四舍五入转换
                lval = Math.round(dval);
            }
        }
    }

    @Override
    public String toString() {
        if(lval != null && dval > 100.0) return lval + " " + unit;
        return dval + " " + unit;
    }

    /**
     * 单位到标准单位的转换系数表
     */
    public static class UnitFactors extends Hashtable<String, Double> {
        private double factor(String unit) {
            Double fatcor = get(unit.toLowerCase());
            if(fatcor == null) throw new NumberFormatException("Invalid unit '" + unit + "'");
            return fatcor;
        }
    }

    /**
     * 可带单位的时间类型
     */
    public static class Time extends NumericWithUnit {
        protected Time() { super(); }
        public Time(int value) { super(value); }
        public Time(long value) { super(value); }
        public Time(float value) { super(value); }
        public Time(double value) { super(value); }
        public Time(String arg) throws NumberFormatException { super(arg); }

        static final UnitFactors unitFactors = new UnitFactors() {{
            put("fs",  1e-15);
            put("ps",  1e-12);
            put("ns",  1e-9);
            put("us",  1e-6);
            put("ms",  1e-3);
            put("s",   1.0);
            put("min", 60.0);
            put("hour", 3600.0);
            put("day",  3600.0 * 24.0);
        }};
        @Override public String getStdUnit() { return "s"; }
        @Override protected double unitFactor(String unit2) { return unitFactors.factor(unit2); }
    }

    /**
     * 可带单位的长度类型
     */
    public static class Length extends NumericWithUnit {
        protected Length() { super(); }
        public Length(int value) { super(value); }
        public Length(long value) { super(value); }
        public Length(float value) { super(value); }
        public Length(double value) { super(value); }
        public Length(String arg) throws NumberFormatException { super(arg); }

        static final UnitFactors unitFactors = new UnitFactors() {{
            put("nm", 1e-9);
            put("um", 1e-6);
            put("mm", 1e-3);
            put("cm", 1e-2);
            put("dm", 1e-1);
            put("m",  1.0);
            put("km", 1e3);
            put("inch", 0.0254);  // 英寸
            put("foot", 0.3048);  // 英尺
            put("yard", 0.9144);  // 码
            put("mile", 1609.344);// 英里
            put("nmile", 1852.0); // 海里
        }};
        @Override public String getStdUnit() { return "m"; }
        @Override protected double unitFactor(String unit2) { return unitFactors.factor(unit2); }
    }

    /**
     * 可带单位的质量类型
     */
    public static class Mass extends NumericWithUnit {
        protected Mass() { super(); }
        public Mass(int value) { super(value); }
        public Mass(long value) { super(value); }
        public Mass(float value) { super(value); }
        public Mass(double value) { super(value); }
        public Mass(String arg) throws NumberFormatException { super(arg); }

        static final UnitFactors unitFactors = new UnitFactors() {{
            put("mg", 1e-3);
            put("g",  1.0);
            put("kg", 1e3);
            put("t",  1e6);
            put("oz", 28.349523125); // 盎司
            put("lb", 453.59237);    // 磅
            put("gr", 0.06479891);   // 格令
        }};
        @Override public String getStdUnit() { return "g"; }
        @Override protected double unitFactor(String unit2) { return unitFactors.factor(unit2); }
    }

    /**
     * 可带单位的质量类型
     */
    public static class Temperature extends NumericWithUnit {
        protected Temperature() { super(); }
        public Temperature(int value) { super(value); }
        public Temperature(long value) { super(value); }
        public Temperature(float value) { super(value); }
        public Temperature(double value) { super(value); }
        public Temperature(String arg) throws NumberFormatException { super(arg); }

        @Override public String getStdUnit() { return "C"; }
        @Override protected double unitFactor(String unit2) { return 0.0; }

        @Override
        public Temperature convertToUnit(String unit2) {
            if(unit.equalsIgnoreCase(unit2)) return this;
            if(unit.equalsIgnoreCase("K")) { dval -= 273.15; /* K -> C */ }
            else if(unit2.equalsIgnoreCase("K")) { dval += 273.15; /* C -> K */ }
            else { throw new NumberFormatException("Invalid unit '" + unit2 + "'"); }
            lval = (dval <= Long.MAX_VALUE) ? Math.round(dval) : null;
            this.unit = unit2;
            return this;
        }
    }

    /**
     * 可带单位的频率类型
     */
    public static class Freq extends NumericWithUnit {
        protected Freq() { super(); }
        public Freq(int value) { super(value); }
        public Freq(long value) { super(value); }
        public Freq(float value) { super(value); }
        public Freq(double value) { super(value); }
        public Freq(String arg) throws NumberFormatException { super(arg); }

        static final UnitFactors unitFactors = new UnitFactors() {{
            put("hz",  1.0);
            put("khz", 1e3);
            put("mhz", 1e6);
            put("ghz", 1e9);
            put("thz", 1e12);
        }};
        @Override public String getStdUnit() { return "Hz"; }
        @Override protected double unitFactor(String unit2) { return unitFactors.factor(unit2); }
    }

    /**
     * 可带单位的电压类型
     */
    public static class Voltage extends NumericWithUnit {
        protected Voltage() { super(); }
        public Voltage(int value) { super(value); }
        public Voltage(long value) { super(value); }
        public Voltage(float value) { super(value); }
        public Voltage(double value) { super(value); }
        public Voltage(String arg) throws NumberFormatException { super(arg); }

        static final UnitFactors unitFactors = new UnitFactors() {{
            put("mv", 1.0);
            put("v",  1e3);
            put("kv", 1e6);
        }};
        @Override public String getStdUnit() { return "mV"; }
        @Override protected double unitFactor(String unit2) { return unitFactors.factor(unit2); }
    }

    /**
     * 可带单位的电流类型
     */
    public static class Current extends NumericWithUnit {
        protected Current() { super(); }
        public Current(int value) { super(value); }
        public Current(long value) { super(value); }
        public Current(float value) { super(value); }
        public Current(double value) { super(value); }
        public Current(String arg) throws NumberFormatException { super(arg); }

        static final UnitFactors unitFactors = new UnitFactors() {{
            put("na", 1e-6);
            put("ua", 1e-3);
            put("ma", 1.0);
            put("a",  1e3);
            put("ka", 1e6);
        }};
        @Override public String getStdUnit() { return "mA"; }
        @Override protected double unitFactor(String unit2) { return unitFactors.factor(unit2); }
    }

    /**
     * 可带单位的电阻类型
     */
    public static class Resistance extends NumericWithUnit {
        protected Resistance() { super(); }
        public Resistance(int value) { super(value); }
        public Resistance(long value) { super(value); }
        public Resistance(float value) { super(value); }
        public Resistance(double value) { super(value); }
        public Resistance(String arg) throws NumberFormatException { super(arg); }

        static final UnitFactors unitFactors = new UnitFactors() {{
            put("ohm",  1.0);
            put("kohm", 1e3);
            put("mohm", 1e6);
        }};
        @Override public String getStdUnit() { return "Ohm"; }
        @Override protected double unitFactor(String unit2) { return unitFactors.factor(unit2); }
    }

    /**
     * 可带单位的电容类型
     */
    public static class Capacitor extends NumericWithUnit {
        protected Capacitor() { super(); }
        public Capacitor(int value) { super(value); }
        public Capacitor(long value) { super(value); }
        public Capacitor(float value) { super(value); }
        public Capacitor(double value) { super(value); }
        public Capacitor(String arg) throws NumberFormatException { super(arg); }

        static final UnitFactors unitFactors = new UnitFactors() {{
            put("pf", 1e-3);
            put("nf", 1.0);
            put("uf", 1e3);
            put("mf", 1e6);
            put("f",  1e9);
        }};
        @Override public String getStdUnit() { return "nF"; }
        @Override protected double unitFactor(String unit2) { return unitFactors.factor(unit2); }
    }

    /**
     * 可带单位的电感类型
     */
    public static class Inductance extends NumericWithUnit {
        protected Inductance() { super(); }
        public Inductance(int value) { super(value); }
        public Inductance(long value) { super(value); }
        public Inductance(float value) { super(value); }
        public Inductance(double value) { super(value); }
        public Inductance(String arg) throws NumberFormatException { super(arg); }

        static final UnitFactors unitFactors = new UnitFactors() {{
            put("ph", 1e-6);
            put("nh", 1e-3);
            put("uh", 1.0);
            put("mh", 1e3);
            put("h",  1e6);
        }};
        @Override protected double unitFactor(String unit2) { return unitFactors.factor(unit2); }
        @Override public String getStdUnit() { return "uH"; }
    }

    /**
     * 可带单位的功率参数类型
     */
    public static class Power extends NumericWithUnit {
        protected Power() { super(); }
        public Power(int value) { super(value); }
        public Power(long value) { super(value); }
        public Power(float value) { super(value); }
        public Power(double value) { super(value); }
        public Power(String arg) throws NumberFormatException { super(arg); }

        static final UnitFactors unitFactors = new UnitFactors() {{
            put("mw", 1.0);
            put("w",  1e3);
            put("kw", 1e6);
            put("hp", 735.49875e3); // 马力
        }};
        @Override public String getStdUnit() { return "mW"; }
        @Override protected double unitFactor(String unit2) { return unitFactors.factor(unit2); }

        @Override
        public Power convertToUnit(String unit2) {
            if(unit.equalsIgnoreCase(unit2)) return this;

            if(unit.equalsIgnoreCase("dBm")) {
                double mW = Math.pow(10, (dval / 10.0)); // dBm -> mW
                double factor2 = unitFactor(unit2);
                dval = mW / factor2;
            } else if(unit2.equalsIgnoreCase("dBm")) {
                double mW = dval * unitFactor(unit);     // ? -> mW
                dval = 10.0 * Math.log10(mW);
            } else {
                double factor = unitFactor(unit);
                double factor2 = unitFactor(unit2);
                dval = dval * factor / factor2;
            }
            lval = (dval <= Long.MAX_VALUE) ? Math.round(dval) : null;
            this.unit = unit2;
            return this;
        }
    }

    public static void main(String[] args) {
        args = new String[] {"30.72e-6THz", "0.03072gHz", "30.72 mhZ", "30720000 hz", "30720000"};
        for(String arg: args) {
            NumericWithUnit.Freq freq = new NumericWithUnit.Freq(arg);
            //System.out.println("  == " + freq);
            freq.convertToStdUnit();
            System.out.println("  == " + freq);
        }

        args = new String[] {"10dbm", "100mW", "12.3W", "0.123KW"};
        for(String arg: args) {
            NumericWithUnit.Power power = new NumericWithUnit.Power(arg);
            power.convertToStdUnit();
            System.out.println("  == " + power);
            power.convertToUnit("dBm");
            System.out.println("  == " + power);
        }
    }
}
